<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');

/**
 * BackendPro
 *
 * A website backend system for developers for PHP 4.3.2 or newer
 *
 * @package         conlabz CMS
 * @author          Christian Schneider
 * @copyright       Copyright (c) 2009
 * @license         
 * @link            http://www.conlabz.de
 * @filesource
 */

// ---------------------------------------------------------------------------

include_once(APPPATH . 'models/Nested_sets_model.php');

/**
 * Menu_model
 *
 * Model used to retrive the menu
 *
 * @package			conlabz CMS
 * @subpackage		Models
 */
class Menu_model extends Base_model {
	function Menu_model( ) {
		parent::Base_model( );

		$this->_TABLES = array( 'Option' => $this->config->item( 'backendpro_table_prefix' ) . 'article' );

		// Cache to store already fetched items
		$this->_CACHE = array( );

		// Object keyword
		// I wouldn't advise changing this, it could corrupt current
		// articles, the reason for needing this is to stop unserialze
		// errors spamming the log files
		$this->object_keyword = "BeP::Object::";
		
		$this->_TABLES['menu'] = "menu";
		$this->menu = new Nested_sets_model();
		$this->menu->setControlParams( $this->_TABLES['menu'] );
		$this->menu->setPrimaryKeyColumn( 'id' );

		log_message( 'debug', 'conlabz CMS : Menu_model class loaded' );
	}

	/**
	 * Get Type Information From Menu By URL
	 *
	 * @access public
	 * @param string url
	 * @return array of menu entries
	 */
	function getTypeArrayByUrl( $menu_url, $website_id ) {
		$this->db->select( 'menu.type,
				    		menu.type_id'
				 			);
		$this->db->from( "menu" );
		$this->db->join( "menu_position", 'menu_position.id = menu.menu_position_id' );
		$this->db->where( '`menu`.`url`', $menu_url );
		$this->db->where( '`menu_position`.`website_id`', $website_id );
		 
		$result = $this->db->get( );
		$row = $result->result_array( );
		if( !empty( $row ) ) {
			return $row[0];
		} //if
	} 
	
	/**
	 * Get Menu Position
	 *
	 * @access public
	 * @param mixed $where Where query string/array
	 * @param array $limit Limit array including offset and limit values
	 * @return object
	 */
	function getMenuPosition( $where = NULL, $limit = array( 'limit' => NULL, 'offset' => '' ) ) {
		$this->db->select( 'menu_position.id,
							menu_position.title,
							position.title AS pos_title'
						   );
		$this->db->from( "menu_position" );
		$this->db->join( "position", 'menu_position.position_id = position.id' );
		$this->db->where( '`menu_position`.`website_id`', $this->session->userdata( 'website_id' ) );
		if( !is_null( $where ) ){
			$this->db->where( $where );
		}
		if( !is_null( $limit['limit'] ) ){
			$this->db->limit( $limit['limit'], ( isset( $limit['offset'] ) ? $limit['offset'] : '' ) );
		}
		$this->db->order_by('menu_position.title asc'); 
		return $this->db->get( );
	}
	
	/**
	 * Get Menu Position ID by Position
	 *
	 * @access public
	 * @param string $where Where query string/array
	 * @return object
	 */
	function getPositionIdByPosition( $string ) {
		$this->db->select( 'menu_position.id'
						   );
		$this->db->from( "menu_position, position" );
		$this->db->where( "`menu_position`.`position_id`=`position`.`id` AND `position`.`title`='".$string."' AND `menu_position`.`website_id`='".$this->session->userdata( 'website_id' )."'" );
		$result = $this->db->get( );
		$row = $result->result_array( );
		return $row[0]['id'];
	}
	
	/**
	 * Get Position
	 *
	 * @access public
	 * @return object
	 */
	function _getPositions( ) {
		$this->db->select( 'position.id,
							position.title'
						   );
		$this->db->from( "position" );

		return $this->db->get( );
	}
	
	/**
	 * Constructs an array of the given positions to be used for a dropdown menu
	 *
	 * @access public
	 * @return array
	 */
	function buildPositionDropdown( ) {
		$positions = $this->_getPositions( );
		
		$dropdown = array( );
		foreach( $positions->result( ) as $row ) {
	    	 $dropdown[$row->id] = $row->title;
		}
		
		return $dropdown;
	}

	/**
	 * Create Pretty Offset
	 *
	 * Creates a pretty text offset to display nested items using basic text
	 *
	 * @access public
	 * @param object $obj Reference to a Nested_sets_model object, e.g. $this->group, $this->resource
	 * @param array $tree Current Tree array
	 * @param string $next_ancestor_sibling String prepended to offset if there is a next sibling after the current ancestor
	 * @param string $no_next_ancestor_sibling String prepended to offset if there is no next sibling after the current ancestor
	 * @param string $last_sibling String appended to offset if this is the last node of the current level
	 * @param string $not_last_siling String appended to offset if this is not the last node of the current level
	 */
	function buildPrettyOffset($obj, $tree,
		$next_ancestor_sibling = "|&nbsp;&nbsp; ",
		$no_next_ancestor_sibling = "&nbsp;&nbsp;&nbsp; ",
		$last_sibling = "`- ",
		$not_last_sibling = "|- ")
	{
		$lvl = $obj->getTreeLevel($tree);

		// Nest the tree
		$offset = '';
		if($lvl > 1)
		{
			$ancestor = $obj->getAncestor($tree['row']);
			/*while( ! $obj->checkNodeIsRoot($ancestor))
			{
				if($obj->checkNodeHasNextSibling($ancestor))
				{
					// Ancestor has sibling so put a | in offset
					$offset = $next_ancestor_sibling . $offset;
				}
				else
				{
					// No next sibling just put space
					$offset = $no_next_ancestor_sibling . $offset;
				}
				$ancestor = $obj->getAncestor($ancestor);
			}*/
		}

		// If this is the last node add branch terminator
		if($obj->checkNodeHasNextSibling($tree['row']))
		{
			$offset .= $not_last_sibling;
		}
		elseif($lvl != 0)
		{
			$offset .= $last_sibling;
		}
		return $offset;
	}	
	
	/**
	 * Build Pretty array for dropdown
	 *
	 * Constructs an array of the given tree to be used for a dropdown menu
	 *
	 * @access public
	 * @param string $tree_id Tree ID, always category
	 * @param string $value_field Table field to use as the option value
	 * @return array
	 */
	function buildMenuDropdown( $tree_id = NULL, $value_field = 'type', $position = 0 )
	{
		if( $tree_id != 'menu' ) {
			show_error( "conlabz CMS->menu_model->buildMenuDropDown: The tree_id for the dropdown must be 'menu'.");
		}

		$obj =& $this->{ $tree_id };
		$tree = $obj->getTreePreorder( $obj->getRoot( ), " AND menu_position_id='".$position."' " );

		$dropdown = array( );
		while( $obj->getTreeNext( $tree ) )	{
			// Get offset
			$offset = $this->buildPrettyOffset( $obj, $tree );
			
			$dropdown[$tree['row'][$value_field]] = $offset . $this->_calculate_menu_title( $tree['row']['id'], $tree['row']['menu_position_id'] );
		}
		return $dropdown;
	}
	
	/**
	 * List for menu
	 *
	 * Constructs an array of the given tree to be used for a unordered list menu
	 *
	 * @access public
	 * @param string $tree_id Tree ID, always category
	 * @param string $value_field Table field to use as the option value
	 * @return array
	 */
	function getMenuList( $position, $lang = 1 ) {
		$sql = "SELECT n.type_id, n.type, n.url, n.id, n.menu_position_id, COUNT(*)-1 AS depth
			      FROM menu AS n, menu AS p
			      WHERE n.lft BETWEEN p.lft AND p.rgt
			      AND n.menu_position_id = ".$position."
			      AND p.menu_position_id = ".$position."
			      GROUP BY n.lft
			      ORDER BY n.lft;";
			      
		//print $sql;
		$query = $this->db->query( $sql );
		$count = 0;
		foreach( $query->result_array( ) as $row ){
		    $data[$count]['type'] 		= $row['type'];
		    $data[$count]['type_id'] 	= $row['type_id'];
		    $data[$count]['depth'] 		= $row['depth'];
		    $data[$count]['title'] 		= $this->_calculate_menu_title( $row['id'], $position, $lang ); //( empty( $row['title'] ) ) ? 'Wurzelknoten' : $row['title']; //
		    $data[$count]['url'] 		= $row['url'];
		    $data[$count]['id'] 		= $row['id'];
		    $count++;
		}
		//echo "<pre>";
		//print_r( $data );
		return $this->_nestify( $data ); 
	}
	
	/**
	 * Calculate menu titel
	 *
	 * @access private
	 * @param string $type
	 * @param string $type_id
	 * @return array
	 */
	function _calculate_menu_title( $id, $position_id, $lang ){
		$this->db->select( 'menu_title.title' );
		$this->db->from( "menu_title" );
		$this->db->where( "menu_title.menu_id", $id );
		$this->db->where( "menu_title.menu_position_id", $position_id );
		$this->db->where( "menu_title.language_id", $lang );  

		$result = $this->db->get( );
		$row = $result->result_array( );
		//@todo: remove from source code
		if( empty( $row ) ){
			return ""; //Wurzelknoten";
		}
		return $row[0]['title'];
	}
	
	/**
	 * Get Article Title
	 *
	 * @access public
	 * @param mixed $where Where query string/array
	 * @param array $limit Limit array including offset and limit values
	 * @return object
	 */
	function _getArticleTitle( $where = NULL ) {
		$this->db->select( 'article_text.menu_title'
						   );
		$this->db->from( "article" );
		$this->db->from( "article_revision" );
		$this->db->from( "article_text" );
		$this->db->from( "category" );
		
		$whereString = "`article`.`id`=`article_text`.`article_id` 
				AND `article`.`category_language_id`=`article_text`.`language_id` 
				AND `article`.`latest_revision_id`=`article_revision`.`id` 
				AND `article_text`.`revision_id`=`article_revision`.`id` 
				AND `article`.`category_id`=`category`.`id`
				AND `category`.`website_id`=" . $this->session->userdata( 'website_id' );
		if( !is_null( $where ) ){
			$whereString .= " AND " . $where;
		}
		$this->db->where( $whereString );
		
		return $this->db->get( );
	}
	
	/**
	 * source: http://semlabs.co.uk/journal/converting-nested-set-model-data-in-to-multi-dimensional-arrays-in-php 
	 *
	 */
	function _nestify( $arrs, $depth_key = 'depth' ){  
		$nested = array();  
   		$depths = array();  
    
		foreach( $arrs as $key => $arr ) {  
			if( $arr[$depth_key] == 0 ) {  
				$nested[$key] = $arr;  
				$depths[$arr[$depth_key] + 1] = $key;  
			} else {  	
				$parent =& $nested;  
				for( $i = 1; $i <= ( $arr[$depth_key] ); $i++ ) {  
					$parent =& $parent[$depths[$i]];  
				}  
				$parent[$key] = $arr;  
				$depths[$arr[$depth_key] + 1] = $key;  
			}  
		}  
    
		return $nested;  
	}  
	
	/**
	 * Insert new menu position
	 *
	 * @access public
	 * @param array $data Data array
	 */
	function insertPosition( $data = array( ) ) {
		$data['website_id'] = $this->session->userdata( 'website_id' );
		//@todo: where does language id came from
		$data['language_id'] = 1;
		//create new menu position entry
		$this->db->set( 'position_id', 		$data['position_id'] );
		$this->db->set( 'website_id', 		$data['website_id'] );
		$this->db->set( 'language_id', 		$data['language_id'] );
		$this->db->set( 'title', 			$data['title'] );
		$this->db->insert( 'menu_position' ); 
		$position_id = $this->db->insert_id( );
		
		//insert into menu the position as a root entry point for further use of nested set moded
		//initial insert 
		$this->db->set( 'id', 				1 );
		$this->db->set( 'menu_position_id',	$position_id );
		$this->db->set( 'type',				'position' );
		$this->db->set( 'type_id',			$position_id );
		$this->db->set( 'lft',				1 );
		$this->db->set( 'rgt',				2 );
		$this->db->set( 'active',			1 );
		$this->db->insert( 'menu' );
	}

	/**
	 * Get maximal Meun ID
	 *
	 * @access public
	 * @param int $id Menu id
	 * @return int
	 */
	function _getMaxMenuId( $position ){
		$this->db->select_max( 'id' );
		$this->db->where( 'menu_position_id', $position ); 
		$query = $this->db->get( 'menu' );
		$row = $query->row( );
		return $row->id;
	}
	
	/**
	 * Insert new menu entry
	 *
	 * @access public
	 * @param array $data Data array
	 */
	function insert( $data = array( ), $position ) {
		// http://www.freude-co.de/2008/07/21/nested-set-model/
		// SELECT @myRight := rgt FROM nested_category WHERE name = 'TELEVISIONS';
		$title = $data['title'];
		unset( $data['title'] );		
		if( empty( $data['menu_id'] ) ){
			$data['lft'] = 1;
			$data['rgt'] = 2;
			unset( $data['menu_id'] );
		} else {
			$this->db->select( 'rgt' );
			$this->db->where( 'id', $data['menu_id'] ); 
			$this->db->where( 'menu_position_id', $position ); 
			$query = $this->db->get( 'menu' );
			$row = $query->row( );
			// UPDATE nested_category SET rgt = rgt + 2 WHERE rgt > @myRight;
			$this->db->query('UPDATE menu SET rgt = rgt + 2 WHERE rgt >= ' . $row->rgt . ' AND menu_position_id='.$position );
			// UPDATE nested_category SET lft = lft + 2 WHERE lft > @myRight;
			$this->db->query('UPDATE menu SET lft = lft + 2 WHERE lft >= ' . $row->rgt . ' AND menu_position_id='.$position );
			// INSERT INTO nested_category(name, lft, rgt) 
			// VALUES('GAME CONSOLES', @myRight + 1, @myRight + 2)
			$data['lft'] = ( $row->rgt );
			$data['rgt'] = ( $row->rgt + 1 );
			$data['id'] = $this->_getMaxMenuId( $position ) + 1;
			unset( $data['menu_id'] );
		}
		//create new category entry	
		$this->db->insert( 'menu', $data );
		
		//add new entry in menu_title
		$this->db->set( 'menu_position_id',	$position );
		$this->db->set( 'menu_id',			$data['id'] );
		//@todo: $this->session->userdata[]
		$this->db->set( 'language_id',		1 );
		$this->db->set( 'title',			$title );
		$this->db->insert( 'menu_title' );
	}
	
	/**
	 * Insert translated menu
	 *
	 * @access public
	 * @param array $data Data array
	 */
	function insert_translation( $data = array( ) ) {
		//create new category entry	
		$this->db->insert( 'menu_title', $data );
	}
	
	/**
	 * Update translated menu
	 *
	 * @access public
	 * @param array $data Data array
	 */
	function update_translation( $title, $data = array( ) ) {
		//create new category entry	
		$this->db->update( 'menu_title', $title, $data );
	}
	
	/**
	 * Insert translated menu
	 *
	 * @access public
	 * @param array $data Data array
	 */
	function get_translation( $data = array( ) ) {
		//create new category entry	
		$this->db->where( $data );
		$result = $this->db->get( 'menu_title' );
		$row = $result->row_array( );
		if( !empty( $row['title'] ) ){
			return $row['title'];
		}
	}
	
	/**
	 * Delete Menu entry
	 *
	 * @access public
	 * @param string $menu Object short name
	 * @param int $position_id Position ID
	 */
	function delete( $url, $position_id ){
		/* from: http://www.klempert.de/nested_sets/#kap5
		 * DELETE FROM tree WHERE lft = $LFT;
		 * UPDATE tree SET lft=lft-1, rgt=rgt-1
		 * 		 WHERE lft BETWEEN $LFT AND $RGT;
		 * UPDATE tree SET lft=lft-2 WHERE lft>$RGT;
		 * UPDATE tree SET rgt=rgt-2 WHERE rgt>$RGT;
		 ***/
		//get rgt value of actual entry
		$this->db->select( 'rgt, lft, id' );
		$this->db->where( 'url', $url ); 
		$this->db->where( 'menu_position_id', $position_id ); 
		$query = $this->db->get( 'menu' );
		$row = $query->row( );
		
		$this->db->delete( 'menu_title', array( 'menu_id' => $row->id, 'menu_position_id' => $position_id ) );
		$this->db->delete( 'menu', array( 'lft' => $row->lft, 'menu_position_id' => $position_id ) );
		$this->db->query( 'UPDATE menu SET lft=lft-1, rgt=rgt-1 WHERE lft BETWEEN ' . $row->lft . ' AND ' . $row->rgt . ' AND menu_position_id=' . $position_id  );
		$this->db->query( 'UPDATE menu SET lft=lft-2 WHERE lft > ' . $row->rgt . ' AND menu_position_id=' . $position_id  );
		$this->db->query( 'UPDATE menu SET rgt=rgt-2 WHERE rgt > ' . $row->rgt . ' AND menu_position_id=' . $position_id  );		
	}
	
	function down( $url, $position ){
		//select left and right from url
		$sql = "SELECT menu.lft, menu.rgt FROM menu WHERE menu.url = '".$url."' AND menu.menu_position_id = '".$position."'";
		$query = $this->db->query( $sql );
		$row = $query->row( );
		//get position 2 higher
		$sql_top = "SELECT menu.url FROM menu WHERE menu.lft = '".( $row->lft + 2 )."' AND menu.rgt = '".( $row->rgt + 2 )."' AND menu.menu_position_id = '".$position."'";
		$query_top = $this->db->query( $sql_top );
		$row_top = $query_top->row( );
		//if not empty we have no subtree
		if( !empty( $row_top ) ){
			//update actual top element
			$sql_update_1 = "UPDATE menu SET menu.lft = ".( $row->lft ).", menu.rgt = ".( $row->rgt )." WHERE menu.url = '".$row_top->url."' AND menu.menu_position_id = '".$position."'";
			$this->db->query( $sql_update_1 );
			//update clicked on element
			$sql_update_2 = "UPDATE menu SET menu.lft = ".( $row->lft + 2 ).", menu.rgt = ".( $row->rgt + 2 )." WHERE menu.url = '".$url."' AND menu.menu_position_id = '".$position."'";
			$this->db->query( $sql_update_2 );
		}
		//@todo: else we have a subtree and need to update all childs
		
	}
	
	function up( $url, $position ){
		//select left and right from url
		$sql = "SELECT menu.lft, menu.rgt FROM menu WHERE menu.url = '".$url."' AND menu.menu_position_id = '".$position."'";
		$query = $this->db->query( $sql );
		$row = $query->row( );
		//get position 2 higher
		$sql_top = "SELECT menu.url FROM menu WHERE menu.lft = '".( $row->lft - 2 )."' AND menu.rgt = '".( $row->rgt - 2 )."' AND menu.menu_position_id = '".$position."'";
		$query_top = $this->db->query( $sql_top );
		$row_top = $query_top->row( );
		//if not empty we have no subtree
		if( !empty( $row_top ) ){
			//update actual top element
			$sql_update_1 = "UPDATE menu SET menu.lft = ".( $row->lft ).", menu.rgt = ".( $row->rgt )." WHERE menu.url = '".$row_top->url."' AND menu.menu_position_id = '".$position."'";
			$this->db->query( $sql_update_1 );
			//update clicked on element
			$sql_update_2 = "UPDATE menu SET menu.lft = ".( $row->lft - 2 ).", menu.rgt = ".( $row->rgt - 2 )." WHERE menu.url = '".$url."' AND menu.menu_position_id = '".$position."'";
			$this->db->query( $sql_update_2 );
		}
		//@todo: else we have a subtree and need to update all childs
	}
	
	function getMenuTitleByUrl( $menu_url, $language_id = '1' ){
		$query = $this->db->query( "SELECT menu_title.title FROM menu_title, menu 
						   WHERE menu.url = '" .$menu_url . "'
						   AND menu_title.language_id = '" . $language_id . "'
						   AND menu.id = menu_title.menu_id 
						   AND menu.menu_position_id = menu_title.menu_position_id" );
		
		$row = $query->result_array( );
		return $row[0]['title'];
	}
	
	function lookup( $url ){
		$return = false;
		$query = $this->db->query( "SELECT menu.id FROM menu, menu_position 
						   WHERE menu.url = '" .$url . "' 
						   AND menu.menu_position_id = menu_position.id
						   AND menu_position.website_id = " . $this->session->userdata( 'website_id' ) );
		
		$row = $query->result_array( );
		if( empty( $row[0] ) )
			$return = true;
		
		return $return;
	}

	function getParent( $url ){
		//position should be removed later
		$query = $this->db->query( "SELECT p.*
									FROM menu AS n, menu AS p, menu_position
									WHERE n.lft BETWEEN p.lft AND p.rgt AND n.url = '" .$url . "'
									AND n.menu_position_id = menu_position.id
						   			AND menu_position.website_id = " . $this->session->userdata( 'website_id' ) ."
						   			AND p.type != 'position' 
									ORDER BY n.lft" );
		
		$row = $query->result_array( );
		if( !empty( $row[0] ) )		
			return $row;
	}
}
/* End of file menu_model.php */
/* Location: ./modules/articles/models/menu_model.php */
